package aiingames.simulation.agent;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;

import aiingames.Config;
import aiingames.simulation.agent.AgentAction.SimpleAction;
import aiingames.simulation.server.Server;

import com.vividsolutions.jts.geom.Coordinate;

public class DummyRLAgent extends Agent implements RLAgent {

	Random rand = new Random();
	private SimpleAction[] actions = { SimpleAction.STOP, SimpleAction.LEFT, SimpleAction.RIGHT };
	private Ball ball;
	private State actualState;

	public State getActualState() {
		return actualState;
	}

	public void setActualState(State actualState) {
		this.actualState = actualState;
	}

	// private int reward = 0;
	public boolean ballInAir;

	private Map<String, State> states = new HashMap<String, State>();
	private TreeMap<Double, SimpleAction> values;
	
	public void putState(State state)
	{
		String stateSig = state.getSignature();
		this.states.put(stateSig, state);
	}

	public DummyRLAgent(int id, Ball b) {
		super(id);
		this.ball = b;
		rand.setSeed(System.currentTimeMillis());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * 
	 * @see
	 * aiingames.simulation.agent.RLAgent#getSimpleAction(aiingames.simulation
	 * .server.Server)
	 */
	@Override
	public SimpleAction getSimpleAction(Server serv) {

		SimpleAction simpleAction = SimpleAction.STOP; // Erstmal nix tun

		// Signatur eines Zustands berechnen
		String stateSig = calculateStateSignature(serv);

		// Zustand aus der Map holen (falls schon mal da gewesen) oder neuen
		// erzeugen

		State state = this.states.get(stateSig);
		if (state == null) {
			// (actualState.getSignature() == state.getSignature())
			state = new State(stateSig);
			this.states.put(stateSig, state);
		}

		// Aktueller State
		actualState = state;

		// greedy: mit 1-EGREEDY wahrscheinlichkeit
		// random: mit EGREEDY wahrscheinlichkeit
		if (Config.EGREEDY < rand.nextDouble()) {

			// Falls mindestens 1 Successor gesammelt wurde
			if (!actualState.getSuccessors().isEmpty()) {
				SimpleAction maxAction = simpleAction;
				double maxValue = 0;

				// System.out.println("-------");

				// System.out.println(actualState.getSuccessors().size());

				for (int i = 0; i < 3; i++) {

					if (actualState.getSuccessors().containsKey(actions[i])) {
						// System.out.println("Action: " +
						// actions[i]+" State signatur "+actualState.getSuccessors().get(actions[i]).getSignature());

						System.out.println(i + "| " + actualState.getSuccessors().get(actions[i]).getSignature() + " | successor value: " + actualState.getSuccessors().get(actions[i]).getValue());

						if (actualState.getSuccessors().get(actions[i]).getValue() > maxValue) {
							maxAction = actions[i];
							maxValue = actualState.getSuccessors().get(actions[i]).getValue();
							// //System.out.println(actions[i] +
							// " <_ non zufall: MV:" + maxValue);

						} else {
							// System.out.println("mv:" + maxValue
							// +"|successor value:" +
							// actualState.getSuccessors().get(actions[i]).getValue()
							// );
						}
					} else {
						// System.out.println("key missing");
						double randomValue = rand.nextDouble() * 1;
						if (randomValue > maxValue) {
							maxAction = actions[i];
							maxValue = randomValue;
							// System.out.println("randomValuePicked");
						}
					}
				}

				// System.out.println("maxAction" + maxAction + "| maxValue: " +
				// maxValue);

				return maxAction;
			}
		}

		return actions[rand.nextInt(3)];

	}

	// Tile- oder Coarse-Coding
	private String calculateStateSignature(Server serv) {

		Coordinate bLoc = serv.getAgentLocation(this.ball);
		Coordinate myLoc = serv.getAgentLocation(this);
		Coordinate velo = serv.getAgentVelocityVector(this.ball);
		int dx = (int) ((bLoc.x - myLoc.x) * 1);
		int dy = (int) ((bLoc.y - myLoc.y) * 1);
		int vx = (int) (velo.x * 1);
		int vy = (int) (velo.y * 1);

		// System.out.println("dx");
		StringBuffer buf = new StringBuffer();
		buf.append(dx);
		buf.append(' ');
		buf.append(dy);
		buf.append(' ');
		buf.append(vx);
		buf.append(' ');
		buf.append(vy);

		String signature = buf.toString();

		int tile = calculateTile(dx, dy, vx, vy);

		ArrayList<String> tmpSigList;
		tmpSigList = Config.tileCodedMap.get(tile) != null ? Config.tileCodedMap.get(tile) : new ArrayList<String>();
		tmpSigList.add(signature);
		Config.tileCodedMap.put(tile, tmpSigList);

		return signature;
	}

	public int calculateReward() {

		int reward;

		if (ballInAir) {
			reward = 1;
		} else {
			reward = -1;
		}
		return reward;
	}

	public void updateValues(State previousState) {

		double value = this.actualState.getValue();
		double alpha = 0.7; // laut literatur: 1/Anzahl tilings
		double gamma = 0.3;

		// Value des letzten States holen
		double previousStateValue = (previousState == null) ? 0 : previousState.getValue();

		// Utility berechnen
		value = value + alpha * (Config.POS_REWARD + gamma * previousStateValue - value) + rand.nextDouble();

		String signature = actualState.getSignature();
		String[] signatureParams = signature.split(" ");
		int key = calculateTile(Integer.parseInt(signatureParams[0]), Integer.parseInt(signatureParams[1]), Integer.parseInt(signatureParams[2]), Integer.parseInt(signatureParams[3]));

		for (String stateSignature : Config.tileCodedMap.get(key)) {
			if (stateSignature != null || stateSignature != "") {
				State state = states.get(stateSignature);
				state.setValue(state.getValue() * (1 - Config.WEIGHT_FACTOR) + (value * Config.WEIGHT_FACTOR) + rand.nextDouble());
			}

		}
		actualState.setValue(value);

	}

	private int calculateTile(int dx, int dy, int vx, int vy) {

		dx = (dx + Config.X_MAX) / Config.STEP_SIZE;
		dy = (dy + Config.Y_MAX) / Config.STEP_SIZE;
		vx = (vx + Config.X_MAX) / Config.STEP_SIZE;
		vy = (vy + Config.Y_MAX) / Config.STEP_SIZE;

		int modulatedKey = dx * 1000000 + dy * 10000 + vx * 100 + vy;

		return modulatedKey;
	}

	@Override
	public AgentAction perform(Server serv) {
		// We don't use this method here
		return null;
	}

	@Override
	public void informEpisodeEnd() {

		System.out.println("BALL DOWN...");
		this.ballInAir = false;
		this.actualState.setValue(-Config.POS_REWARD);

	}

	@Override
	public Map<String, State> getStateMap() {
		return this.states;
	}

	@Override
	public void setStateMap(Map<String, State> states) {
		this.states = states;
	}

	@Override
	public void setBallInAir(boolean value) {
		this.ballInAir = value;
	}

	// @Override
	// public void updateValues(double value) {
	//
	// String signature = actualState.getSignature();
	//
	// String[] params = signature.split(" ");
	//
	// int key = calculateTile(Integer.parseInt(params[0]),
	// Integer.parseInt(params[1]), Integer.parseInt(params[2]),
	// Integer.parseInt(params[3]));
	//
	// for (String stateSignature : Config.tileCodedMap.get(key)) {
	// if (stateSignature != null || stateSignature != "") {
	// State state = states.get(stateSignature);
	// state.setValue(value * Config.WEIGHT_FACTOR);
	// }
	// }
	//
	// actualState.setValue(value);
	// }
}